Skip to content

LightGBM 학습 파이프라인 구축 - #14

Open
cfcromn wants to merge 6 commits into
developfrom
feature/lightgbm-training
Open

LightGBM 학습 파이프라인 구축#14
cfcromn wants to merge 6 commits into
developfrom
feature/lightgbm-training

Conversation

@cfcromn

@cfcromn cfcromn commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

✨ 작업 내용

이번 PR에서 어떤 작업을 했는지 간단히 요약해주세요.

스텁이던 트레이딩 모델을 실제 LightGBM 모델로 교체했습니다. 시세를 수집해 피처와 라벨을 만들고, 시계열 홀드아웃으로 검증한 모델을 학습합니다.

  • 피처 계산 모듈 (app/mlops/features.py) — 학습과 추론이 공유하는 계약 원본
  • 시세 수집 + 라벨링 (app/mlops/dataset.py) — Yahoo Finance, 키 불필요
  • 학습 + 백테스트 (app/mlops/train.py) — python -m app.mlops.train
  • predictor 버그 수정 — 피처를 이름 순서대로 재배열
  • CLAUDE.md에 피처 계약 표 추가

🔍 리뷰 시 참고사항

  • 리뷰어가 알면 좋은 변경 이유, 배경, 고려했던 점 등을 적어주세요.

⚠️ 발견한 버그를 같이 고쳤습니다 — 이게 이 PR의 가장 중요한 부분

LightGBM Booster는 피처 이름이 아니라 열 순서로 매칭합니다. pd.DataFrame 을 넘겨도 이름을 보지 않습니다. 실제로 확인한 동작:

입력 기존 코드의 결과
정상 순서 0.972182
dict 키 순서만 역순 0.907763에러 없이 다른 값
volumeChangevolume_change 오타 0.972182조용히 통과
피처 누락/추가 LightGBMError (개수만 검사)

즉 기존 predict() 는 Spring이 보내는 JSON 필드 순서에 의존했습니다. 스텁이 0.5 를 반환하는 동안은 드러나지 않지만, 실모델을 붙이는 순간 순서가 어긋나면 에러 없이 틀린 매매 결정이 됩니다. 그래서 실모델 투입과 같은 PR에서 고쳤습니다.

수정: booster.feature_name() 순서로 명시적으로 재배열하고 누락된 이름은 ValueError. 회귀 테스트 5개가 재발을 막습니다.

⚠️ 실모델을 붙이면 봇이 사실상 아무것도 하지 않습니다

머지 전에 판단이 필요한 부분입니다. 학습된 모델의 확률 분포는 0.382 ~ 0.755 (중앙값 0.548)에 몰려 있는데, 현재 THRESHOLDS 는 그 바깥을 보고 있습니다:

시장/전략 BUY SELL HOLD
US STABLE 1.42% 0% 98.58%
US AGGRESSIVE 68.85% 0% 31.15%
US TREND 21.49% 0% 78.51%
COIN STABLE 0.07% 0% 99.93%
COIN AGGRESSIVE 7.50% 0.21% 92.29%
COIN TREND 0.21% 0% 99.79%

SELL 은 구조적으로 발생하지 않습니다. SELL 임계값(0.250.45)이 모델의 최소 확률(0.382)보다 낮습니다. 라벨이 "5일 후 상승"이고 데이터의 상승 비율이 0.520.56이라 모델 출력이 0.5 위쪽으로 편향되는데, 이건 임계값 조정만으로는 완전히 풀리지 않습니다.

THRESHOLDS 재튜닝은 계획에서 범위 밖으로 뒀기 때문에 값을 건드리지 않고 그대로 뒀습니다. 백테스트 결과(models/metrics.json 의 임계값별 정밀도/커버리지)가 튜닝 근거로 나오니, 별도 작업으로 진행하는 게 맞다고 봅니다. 다르게 생각하시면 말씀해 주세요.

피처 정의를 백엔드와 합의해야 합니다

ma5 / ma20 / macd종가로 나눈 비율입니다. 절대값으로는 한 모델이 두 시장을 학습할 수 없습니다:

AAPL BTC-USD
ma20 절대값 311.50 68,221.11
ma20 / close 1.0052 0.8655

이름과 개수가 맞아도 정의가 다르면 모델은 조용히 틀린 예측을 냅니다. 위 순서 버그를 고쳐도 이건 안 잡힙니다. CLAUDE.md 의 Feature Contract 표와 app/mlops/features.py 가 계약 원본입니다.

데이터 소스가 Yahoo Finance 하나뿐입니다

이 환경에서 Binance·Coinbase·Kraken·stooq 는 전부 연결이 막혀 있었습니다(curlcode=000). Yahoo Finance 만 열려 있고 키 없이 US·COIN 10년치를 줍니다. urllib 이 시스템 CA 를 못 찾아 certifi 를 명시해야 하는 함정도 코드에 반영했습니다.

지표 계산에 ta 같은 라이브러리를 넣지 않았습니다 — 6개 지표는 각각 두세 줄이고, 새 의존성은 Spring 이 재현해야 할 공식을 한 겹 감춥니다. 추가한 의존성은 scikit-learn(AUC) 하나입니다.

검증 결과

CI 스모크     py_compile / app.title == 'FastAPI'   OK
테스트        39 passed (기존 23 + 신규 16)

학습 — 12종목 10년치, 32,201행:

전체 32201행 | 학습 25325행 | 홀드아웃 6876행 | 분할 기준 2024-08-24
best_iteration: 126

train   : auc 0.6278  accuracy 0.5955
holdout : auc 0.5394  accuracy 0.5391

홀드아웃 AUC 0.5394 는 금융 시계열에서 정상 범위입니다. 0.7 이상이면 오히려 누수를 의심해야 하는데, 학습 스크립트가 두 경우 모두 경고를 냅니다. 시계열 분할이라 랜덤 split 의 미래 정보 누수는 없고, 라벨 꼬리 horizon 행도 잘라냅니다(테스트로 검증).

실모델 엔드포인트:

요청 응답
US/STABLE {"probability":0.6306689733244655,"modelVersion":"trading_lgbm","action":"HOLD"}
같은 값, 키 순서만 역순 0.6306689733244655소수점 16자리까지 동일
US/AGGRESSIVE action":"BUY" (임계값 0.55 통과)
피처 오타 500
추천 (COIN 후보 2개) probability: 0.4808

decision_logs0.5 가 아닌 실제 확률이 남는 것도 확인했습니다:

 id | decision_type  | market | ai_strategy | model_output_probability | action | model_version
  1 | TRADING        | US     | STABLE      |                   0.6307 | HOLD   | trading_lgbm
  3 | TRADING        | US     | AGGRESSIVE  |                   0.6307 | BUY    | trading_lgbm
  4 | RECOMMENDATION | COIN   |             |                   0.4808 | BUY    | trading_lgbm

모델 파일은 git 에 넣지 않았습니다

models/ 를 gitignore 했습니다. 학습을 돌려야 생기고, 없으면 기존 스텁으로 폴백하므로 CI 와 로컬 기동은 그대로 됩니다. MLflow 를 붙이면 그때 아티팩트 저장소가 생깁니다.


✅ 체크리스트

  • 문서(README, .env.example 등) 변경이 필요한 경우 작성 또는 수정했나요?
  • 작업한 코드가 정상적으로 동작하는 것을 직접 확인했나요?
  • 필요한 경우 테스트 코드를 작성하거나 수정했나요?
  • Merge 대상 브랜치를 올바르게 설정했나요?
  • PR에 관련 없는 작업이 포함되지 않았나요?
  • 적절한 라벨과 리뷰어를 설정했나요? (라벨만 설정, 리뷰어 미지정)

📎 관련 이슈(선택)

@cfcromn cfcromn added ✅ Test Test관련사항 ✨ Feature 신규 기능 labels Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 신규 기능 ✅ Test Test관련사항

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant